/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
/*
* gtrstatustextview.c
* Copyright (C) Jonas Hagmar 2009 <gnotterdev@gmail.com>
* 
* gtrstatustextview.c is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* gtrstatustextview.c is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along
* with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtrstatustextview.h"
#include "gtruritexttag.h"

G_DEFINE_TYPE (GtrStatusTextView, gtr_status_text_view, GTK_TYPE_TEXT_VIEW);

typedef struct _GtrStatusTextViewPrivate GtrStatusTextViewPrivate;

struct _GtrStatusTextViewPrivate
{
  GtrUriTextTag *last_tag;
};

#define GTR_STATUS_TEXT_VIEW_GET_PRIVATE(o)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTR_TYPE_STATUS_TEXT_VIEW, GtrStatusTextViewPrivate))

static gboolean
tag_event (GtrUriTextTag * tag,
	   GObject * object,
	   GdkEvent * event, GtkTextIter * iter, gpointer user_data)
{
  GError *error = NULL;
  gchar const *uri;

  if (event->type == GDK_BUTTON_PRESS)
    {
      uri = gtr_uri_text_tag_get_uri (tag);
      if (!g_app_info_launch_default_for_uri (uri, NULL, &error))
	{
	  g_warning ("Launching failed: %s\n", error->message);
	  g_error_free (error);
	}
    }

  return FALSE;
}

static gboolean
motion_notify (GtkWidget * widget, GdkEventMotion * event, gpointer user_data)
{
  gint buffer_x;
  gint buffer_y;
  GtkTextIter iter;
  GSList *tag_list;
  GSList *tag_list_element;
  GdkCursor *cursor;
  GdkWindow *window;
  GtrUriTextTag *tag = NULL;
  gboolean hovering = FALSE;

  GtkTextView *view = GTK_TEXT_VIEW (widget);
  GtrStatusTextView *status_view = GTR_STATUS_TEXT_VIEW (widget);
  GtrStatusTextViewPrivate *priv =
    GTR_STATUS_TEXT_VIEW_GET_PRIVATE (status_view);

  gtk_text_view_window_to_buffer_coords (view,
					 GTK_TEXT_WINDOW_WIDGET,
					 (gint) event->x,
					 (gint) event->y,
					 &buffer_x, &buffer_y);

  gtk_text_view_get_iter_at_location (view, &iter, buffer_x, buffer_y);

  tag_list = gtk_text_iter_get_tags (&iter);

  if (tag_list != NULL)
    {
      tag_list_element = tag_list;
      while (tag_list_element)
	{
	  if (GTR_IS_URI_TEXT_TAG (tag_list_element->data))
	    {
	      tag = (GtrUriTextTag *) tag_list_element->data;
	      hovering = TRUE;
	      break;
	    }
	  tag_list_element = tag_list_element->next;
	}

      g_slist_free (tag_list);
    }

  if (hovering)
    cursor = gdk_cursor_new (GDK_HAND2);
  else
    cursor = gdk_cursor_new (GDK_XTERM);

  if (tag != priv->last_tag)
    {
      if (priv->last_tag != NULL)
	{
	  gtr_uri_text_tag_set_underlined (priv->last_tag, FALSE);
	}
      if (tag != NULL)
	{
	  gtr_uri_text_tag_set_underlined (tag, TRUE);
	}
      priv->last_tag = tag;
    }

  window =
    gtk_text_view_get_window (GTK_TEXT_VIEW (view), GTK_TEXT_WINDOW_TEXT);
  gdk_window_set_cursor (window, cursor);
  gdk_cursor_unref (cursor);

  return FALSE;
}

static gboolean
leave_notify (GtkWidget * widget,
	      GdkEventCrossing * event, gpointer user_data)
{
  GtrStatusTextView *status_view = GTR_STATUS_TEXT_VIEW (widget);
  GtrStatusTextViewPrivate *priv =
    GTR_STATUS_TEXT_VIEW_GET_PRIVATE (status_view);

  if (priv->last_tag != NULL)
    {
      gtr_uri_text_tag_set_underlined (priv->last_tag, FALSE);
      priv->last_tag = NULL;
    }

  return FALSE;
}

static void
realize (GtrStatusTextView * view, gpointer user_data)
{
  GdkWindow *window =
    gtk_text_view_get_window (GTK_TEXT_VIEW (view), GTK_TEXT_WINDOW_TEXT);
  GdkEventMask mask;
  mask = gdk_window_get_events (window);
  gdk_window_set_events (window, mask | GDK_LEAVE_NOTIFY_MASK);
}

static void
gtr_status_text_view_init (GtrStatusTextView * view)
{
  GtrStatusTextViewPrivate *priv = GTR_STATUS_TEXT_VIEW_GET_PRIVATE (view);

  priv->last_tag = NULL;

  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), GTK_WRAP_WORD_CHAR);
  gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
  gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 5);
  g_signal_connect (view, "motion-notify-event", G_CALLBACK (motion_notify),
		    NULL);
  g_signal_connect (view, "leave-notify-event", G_CALLBACK (leave_notify),
		    NULL);
  g_signal_connect (view, "realize", G_CALLBACK (realize), NULL);
}

static void
gtr_status_text_view_finalize (GObject * object)
{
  /* TODO: Add deinitalization code here */

  G_OBJECT_CLASS (gtr_status_text_view_parent_class)->finalize (object);
}

static void
gtr_status_text_view_class_init (GtrStatusTextViewClass * klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  /*GtkTextViewClass* parent_class = GTK_TEXT_VIEW_CLASS (klass); */

  g_type_class_add_private (klass, sizeof (GtrStatusTextViewPrivate));

  object_class->finalize = gtr_status_text_view_finalize;
}

GtkWidget *
gtr_status_text_view_new (void)
{
  return g_object_new (GTR_TYPE_STATUS_TEXT_VIEW, NULL);
}

static gchar const *at_regexp = "(^|\\s)(@\\w+)";
static gchar const *url_regexp =
  "(^|\\s)((https?|ftp|gopher|telnet|file|notes|ms-help):((//)|(\\\\\\\\))+[\\w\\d:#@%/;$()~_?\\+-=\\\\\\.&]*)";

static void
insert_at (GtkTextBuffer * buffer,
	   GtkTextTagTable * tag_table,
	   char const *status, gint last_pos, gint start_pos, gint end_pos)
{
  GtkTextIter end;
  GtrUriTextTag *tag;
  char *str;
  char *addr;

  if (start_pos > last_pos)
    {
      gtk_text_buffer_get_end_iter (buffer, &end);
      gtk_text_buffer_insert (buffer, &end, status + last_pos,
			      start_pos - last_pos);
    }

  gtk_text_buffer_get_end_iter (buffer, &end);
  tag = gtr_uri_text_tag_new ();
  str = g_strndup (status + start_pos + 1, end_pos - start_pos - 1);
  addr = g_strdup_printf ("http://www.twitter.com/%s", str);
  g_free (str);
  gtr_uri_text_tag_set_uri (tag, addr);
  g_free (addr);
  gtr_uri_text_tag_set_color (tag, "blue");
  gtk_text_tag_table_add (tag_table, GTK_TEXT_TAG (tag));
  g_signal_connect (tag, "event", G_CALLBACK (tag_event), NULL);
  gtk_text_buffer_insert_with_tags (buffer,
				    &end,
				    status + start_pos,
				    end_pos - start_pos,
				    GTK_TEXT_TAG (tag), NULL);
  g_object_unref (tag);
}

static void
insert_url (GtkTextBuffer * buffer,
	    GtkTextTagTable * tag_table,
	    char const *status, gint last_pos, gint start_pos, gint end_pos)
{
  GtkTextIter end;
  GtrUriTextTag *tag;
  char *str;

  if (start_pos > last_pos)
    {
      gtk_text_buffer_get_end_iter (buffer, &end);
      gtk_text_buffer_insert (buffer, &end, status + last_pos,
			      start_pos - last_pos);
    }

  gtk_text_buffer_get_end_iter (buffer, &end);
  tag = gtr_uri_text_tag_new ();
  str = g_strndup (status + start_pos, end_pos - start_pos);
  gtr_uri_text_tag_set_uri (tag, str);
  g_free (str);
  gtr_uri_text_tag_set_color (tag, "blue");
  gtk_text_tag_table_add (tag_table, GTK_TEXT_TAG (tag));
  g_signal_connect (tag, "event", G_CALLBACK (tag_event), NULL);
  gtk_text_buffer_insert_with_tags (buffer,
				    &end,
				    status + start_pos,
				    end_pos - start_pos,
				    GTK_TEXT_TAG (tag), NULL);
  g_object_unref (tag);
}

void
gtr_status_text_view_set_data (GtrStatusTextView * view,
			       gchar const *user,
			       gchar const *status,
			       gchar const *created_at,
			       gchar const *source_href,
			       gchar const *source_name, gchar const *id)
{
  GtrUriTextTag *tag;
  GtkTextTag *gray_tag;
  GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
  GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (buffer);
  GRegex *regex_url, *regex_at;
  GMatchInfo *match_info_url, *match_info_at;
  GtkTextIter end;
  gint last_pos = 0;
  gint start_pos_at, end_pos_at;
  gint start_pos_url, end_pos_url;
  gchar *addr;

  gtk_text_buffer_get_end_iter (buffer, &end);
  tag = gtr_uri_text_tag_new ();
  addr = g_strdup_printf ("http://www.twitter.com/%s", user);
  gtr_uri_text_tag_set_uri (tag, addr);
  g_free (addr);
  gtr_uri_text_tag_set_bold (tag, TRUE);
  gtr_uri_text_tag_set_color (tag, "dark green");
  gtk_text_tag_table_add (tag_table, GTK_TEXT_TAG (tag));
  g_signal_connect (tag, "event", G_CALLBACK (tag_event), NULL);
  gtk_text_buffer_insert_with_tags (buffer,
				    &end, user, -1, GTK_TEXT_TAG (tag), NULL);
  g_object_unref (tag);

  gtk_text_buffer_get_end_iter (buffer, &end);
  gtk_text_buffer_insert (buffer, &end, " ", -1);

  regex_at = g_regex_new (at_regexp, 0, 0, NULL);
  g_regex_match (regex_at, status, 0, &match_info_at);
  regex_url = g_regex_new (url_regexp, 0, 0, NULL);
  g_regex_match (regex_url, status, 0, &match_info_url);

  while (1)
    {
      if (g_match_info_matches (match_info_at))
	{
	  if (g_match_info_matches (match_info_url))
	    {
	      g_match_info_fetch_pos (match_info_at, 2, &start_pos_at,
				      &end_pos_at);
	      g_match_info_fetch_pos (match_info_url, 2, &start_pos_url,
				      &end_pos_url);
	      if (start_pos_at < start_pos_url)
		{
		  insert_at (buffer, tag_table, status, last_pos,
			     start_pos_at, end_pos_at);
		  last_pos = end_pos_at;
		  g_match_info_next (match_info_at, NULL);
		}
	      else
		{
		  insert_url (buffer, tag_table, status, last_pos,
			      start_pos_url, end_pos_url);
		  last_pos = end_pos_url;
		  g_match_info_next (match_info_url, NULL);
		}
	    }
	  else
	    {
	      g_match_info_fetch_pos (match_info_at, 2, &start_pos_at,
				      &end_pos_at);
	      insert_at (buffer, tag_table, status, last_pos, start_pos_at,
			 end_pos_at);
	      last_pos = end_pos_at;
	      g_match_info_next (match_info_at, NULL);
	    }
	}
      else if (g_match_info_matches (match_info_url))
	{
	  g_match_info_fetch_pos (match_info_url, 2, &start_pos_url,
				  &end_pos_url);
	  insert_url (buffer, tag_table, status, last_pos, start_pos_url,
		      end_pos_url);
	  last_pos = end_pos_url;
	  g_match_info_next (match_info_url, NULL);
	}
      else
	{
	  break;
	}
    }

  g_match_info_free (match_info_at);
  g_match_info_free (match_info_url);
  g_regex_unref (regex_at);
  g_regex_unref (regex_url);

  gtk_text_buffer_get_end_iter (buffer, &end);
  gtk_text_buffer_insert (buffer, &end, status + last_pos, -1);

  gray_tag = gtk_text_buffer_create_tag (buffer,
					 "gray_tag",
					 "pixels-above-lines",
					 3,
					 "pixels-below-lines",
					 5,
					 "style",
					 PANGO_STYLE_ITALIC,
					 "foreground",
					 "dark gray",
					 "family", "Times", NULL);

  gtk_text_buffer_get_end_iter (buffer, &end);
  gtk_text_buffer_insert (buffer, &end, "\n", -1);

  gtk_text_buffer_get_end_iter (buffer, &end);
  tag = gtr_uri_text_tag_new ();
  addr = g_strdup_printf ("http://www.twitter.com/%s/status/%s", user, id);
  gtr_uri_text_tag_set_uri (tag, addr);
  g_free (addr);
  gtk_text_tag_table_add (tag_table, GTK_TEXT_TAG (tag));
  g_signal_connect (tag, "event", G_CALLBACK (tag_event), NULL);
  gtk_text_buffer_insert_with_tags (buffer,
				    &end,
				    created_at,
				    -1, GTK_TEXT_TAG (tag), gray_tag, NULL);
  g_object_unref (tag);

  gtk_text_buffer_get_end_iter (buffer, &end);
  gtk_text_buffer_insert_with_tags (buffer,
				    &end, " from ", -1, gray_tag, NULL);

  if (source_href)
    {
      gtk_text_buffer_get_end_iter (buffer, &end);
      tag = gtr_uri_text_tag_new ();
      gtr_uri_text_tag_set_uri (tag, source_href);
      gtk_text_tag_table_add (tag_table, GTK_TEXT_TAG (tag));
      g_signal_connect (tag, "event", G_CALLBACK (tag_event), NULL);
      gtk_text_buffer_insert_with_tags (buffer,
					&end,
					source_name,
					-1,
					GTK_TEXT_TAG (tag), gray_tag, NULL);
      g_object_unref (tag);
    }
  else
    {
      gtk_text_buffer_get_end_iter (buffer, &end);
      gtk_text_buffer_insert_with_tags (buffer,
					&end,
					source_name, -1, gray_tag, NULL);
    }
}
